import debounce from 'lodash/debounce';
import { LockFunction } from '../utils/structure';

function makeScroll() {
  const list = [];

  function calculateDistanceWithWindow() {
    const { scrollY, outerHeight, document } = window;
    return document.body.scrollHeight - scrollY - outerHeight;
  }

  function calculateDistanceWithElement(container) {
    const st = container.scrollTop;
    const sh = container.scrollHeight;
    const h = container.clientHeight;
    return sh - st - h;
  }

  function calculateDistance(container) {
    if (container === window) {
      return calculateDistanceWithWindow();
    } else {
      return calculateDistanceWithElement(container);
    }
  }

  function getOptions(bindings) {
    const { value, arg } = bindings;
    if (typeof value !== 'function') {
      throw new TypeError('v-scroll callback must be a function');
    }
    const callback = LockFunction(value);
    const intArg = parseInt(arg, 10);
    const threshold = Number.isNaN(intArg) ? 20 : intArg;
    return { callback, threshold };
  }

  function getScrolllHandler({ callback, threshold, container }) {
    let lastDistance = 0;
    return debounce(function handler() {
      const distance = calculateDistance(container);
      const scrollVector = distance - lastDistance;
      lastDistance = distance;
      if (distance < threshold && scrollVector < 0) {
        callback(distance);
      }
    }, 10, { trailing: true });
  }

  function bind(el, bindings) {
    if (process.server) return;
    if (list.some(t => t.el === el)) return;
    const { modifiers } = bindings;
    const container = modifiers.self ? el : window;
    const handler = getScrolllHandler({ ...getOptions(bindings), container });
    list.push({ handler, container, el });
    container.addEventListener('scroll', handler);
  }

  function unbind(el) {
    if (process.server) return;
    const target = list.find(t => t.el === el);
    if (target) {
      const { container, handler } = target;
      container.removeEventListener('scroll', handler);
    }
  }

  return { bind, unbind };
}

export default makeScroll();



